<?php
/*
  palanganazo  
  http://code.google.com/p/palanganazo/
  
  by >> s E t H <<
  xd dot seth at gmail
  http://xd-blog.com.ar/
*/

    error_reporting(0);
    define('DEBUGFILE', 'debug.txt'); // Leave it blank to output debug information to stdout
    define('DEBUG', false);
    
    if ( DEBUG ) {
        error_reporting(99999999);
    }
    
    $options = getopt('', array('load:', 'forks:', 'passwords:'));

    // File with passwords
    if( !isset($options['passwords']) ){
        display_help();
        exit();
    }

    // The plugin
    if ( isset($options['load']) ) {
        include( $options['load'] . '.php');
    } else {
        display_help();
        exit();
    }

    $max_forks = isset($options['forks']) ? $options['forks'] : '50';
   
    // Check if all the arguments required by the plugin are present
    if ( sizeof(getopt('', $plugin_required_options)) != sizeof($plugin_required_options) ) {
        display_help();
        echo "\n  Plugin specific:\n";
        plugin_display_help();
        exit();
    }

    $fp           = fopen($options['passwords'], 'r');
    $children     = array(); // Create an array which will contain the PIDs of the children
    $no_passwords = false; // Will be true after the last password has been readed
    $passwords_not_tested_file = tempnam('', ''); // File containing all the not tested passwords
    
    while ( 1 ) { // Will stop with exit() or when the process is killed by a child
        if( (sizeof($children) < $max_forks) ) { 
            if ( $password = fgets($fp) ) {
                $password = rtrim($password);
                
                $pid = pcntl_fork();
                
                if ( $pid == -1 ) {
                    // Something went wrong 
                    show_debug('Can\'t fork! Password ' . $password . ' not tested');
                    password_not_tested($password);
                    
                } elseif ( $pid == 0 ) {
                    // This part is only executed in the child
                    unset($children, $fp, $threads, $diccionario);
                    
                    if ( plugin_crack() !== false ){
                        //Cracked!!1!
                        echo 'La palangana: '.$password."\n";
                        show_debug('Clave ' . $password . ' CORRECTA');
                        posix_kill(posix_getppid(), 15); // sigterm to kill the parent, so it stops trying more passwords
                    } else {
                        show_debug('Clave ' . $password . ' incorrecta');
                    }
                    exit(); // The child dies, becoming a zombie
                    
                } else {
                    // This part is only executed in the parent
                    show_debug('Children created with pid ' . $pid . ' for password ' . $password);
                    $children[] = $pid; // To know hoy many childrens are alive
                }
            } else {
                $no_passwords = true; // No more passwords to read
            }
        }
        
        // When children die, this gets rid of the zombies
        while( pcntl_wait($status, WNOHANG OR WUNTRACED) > 0 ) {
             usleep(5000);
        }
        
        // We want to handle the death of the children and get them out of the $children array.
        while( list($key, $val) = each($children) ) {
             if( !posix_kill($val, 0) ) { // This detects if the child is still running or not
                unset($children[$key]);
            }
        }
        $children = array_values($children); // Reindex the array
        
        //No more passwords or childrens
        if ( $no_passwords and (sizeof($children) == 0) ) {
            echo 'Password not found'."\n";
            if( file_get_contents($passwords_not_tested_file) ) {
                echo 'Some passwords weren\'t tested. Check ' . $passwords_not_tested_file . "\n";
            }
            exit();
        }
        
    }
    
    // Show or save the debug information
    function show_debug ($que) {
        if ( DEBUG ){
            if ( !file_put_contents(DEBUGFILE, time() . '| ' . $que . "\n", FILE_APPEND) ) {
                echo time() . '| ' . $que . "\n";
            }
        }
    }
    
    function display_help() {
        echo ": :: palanganazo :: :\n",
             "  http://code.google.com/p/palanganazo/\n",
             "\n",
             "  --load=<file>                 Loads the plugin file.php\n",
             "  --passwords=<file.txt>        Loads passwords from file.txt\n",
             "  --forks=<number>              How many parallel forks (default: 50)\n"
             ;
    }
    
    // Sometimes, a few passwords aren't tested. This function saves them into a file.
    function password_not_tested ($password) {
        global $passwords_not_tested_file;
        
        file_put_contents($passwords_not_tested_file, $password . "\n", FILE_APPEND);
    }
?>